package com.pshare.session.impl.redis;

import java.io.IOException;
import java.io.Serializable;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import com.pshare.session.Session;
import com.pshare.session.SessionFactory;
import com.pshare.session.Trackable;
import com.pshare.session.impl.DefaultSessionImpl;
import com.pshare.session.utils.SerializeUtil;
import com.pshare.session.utils.UUIDUtil;

//@Component("appSessionFactory")
public class RedisSessionFactoryImpl implements SessionFactory {

	//@Resource(name="sessionRedisTemplate")
	private RedisTemplate<Serializable, Serializable> template;

	/** 会话过期时间 */
	//private static Long expireTime = 1209600L;
	
	public RedisSessionFactoryImpl() {
		super();
	}
	
	public RedisSessionFactoryImpl(RedisTemplate<Serializable, Serializable> redisTemplate) {
		this(redisTemplate, null);
	}

	public RedisSessionFactoryImpl(RedisTemplate<Serializable, Serializable> redisTemplate, Long expireTime) {
		super();
		if (redisTemplate == null)
			throw new NullPointerException("redisTemplate is null.");
		this.template = redisTemplate;
		
//		if (expireTime != null && expireTime > 0)
//			this.expireTime = expireTime;
	}
	
	public Session newSession() {
		Session session = this.createSession();
		this.put(session);
		return session;
	}

	protected Session createSession() {
		return new DefaultSessionImpl(UUIDUtil.getUUID(), this);
	}

	protected Session createSession(String id) {
		return new DefaultSessionImpl(id, this);
	}

	@Override
	public Session getSession(Trackable trackable) {
		if (trackable == null)
			throw new NullPointerException("trackable is null.");
		String trackId = trackable.getTrackId();
		Session session = null;
		if (StringUtils.isBlank(trackId)) {
			session = this.createSession();
			this.put(session);
			return session;
		}
		session = this.getSession(trackable.getTrackId());
		if (session == null) {
			session = this.createSession(trackable.getTrackId());
			this.put(session);
			return session;
		} else {
			session.setFactory(this);
		}

		return session;
	}
	
    @Override
    public Session readSession(Trackable trackable) {
        if (trackable == null)
            throw new NullPointerException("trackable is null.");
        String trackId = trackable.getTrackId();
        Session session = null;
        if (StringUtils.isBlank(trackId)) {
        	throw new NullPointerException("sessionId is null.");
        }
        session = this.getSession(trackable.getTrackId());
        if (session != null) {
        	session.setFactory(this);
        }

        return session;
    }

	/**
	 * <p>
	 * 根据追踪ID从redis中获取会话对象。
	 * </p>
	 * 
	 * @param trackId
	 *            追踪ID
	 * @param session
	 * @return Session
	 */
	private Session getSession(final String trackId) {
		Session session = null;
		if (StringUtils.isBlank(trackId))
			throw new IllegalArgumentException("trackId is blank.");

		session = (Session) template.execute(new RedisCallback<Serializable>() {
			@Override
			public Serializable doInRedis(RedisConnection connection) throws DataAccessException {
				byte[] key_ = null;
				try {
					key_ = SerializeUtil.getBytesFromObject(trackId);
				} catch (IOException e1) {
					e1.printStackTrace();
				}
				if (connection.exists(key_)) {
					byte[] value = connection.get(key_);
					try {
						return (Serializable) SerializeUtil.getObjectFromBytes(value);
					} catch (ClassNotFoundException | IOException e) {
						e.printStackTrace();
					}
				}
				return null;
			}
		});

		return session;
	}

	@Override
	public void put(final Session session) {
		if (session == null)
			throw new NullPointerException("session is null.");

		template.execute(new RedisCallback<Object>() {
			@Override
			public Object doInRedis(RedisConnection connection) throws DataAccessException {
				try {
					byte[] key_ = SerializeUtil.getBytesFromObject(session.getId());
					byte[] value_ = SerializeUtil.getBytesFromObject(session);
					//connection.setEx(key_, expireTime, value_);
					connection.set(key_, value_);
					return true;
				} catch (IOException e) {
					e.printStackTrace();
				}
				return false;
			}
		});
	}

	@Override
	public void remove(final Trackable trackable) {
		if (trackable == null || StringUtils.isBlank(trackable.getTrackId()))
			throw new IllegalArgumentException("trackable is null or trackId is blank.");
		
		template.execute(new RedisCallback<Long>() {
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				byte[] key_ = null;
				try {
					key_ = SerializeUtil.getBytesFromObject(trackable.getTrackId());
				} catch (IOException e1) {
					e1.printStackTrace();
				}
				long result = connection.del(key_);
				return result;
			}
		});
	}

	@Override
	public void flush() {
		template.execute(new RedisCallback<Object>() {
			@Override
			public Object doInRedis(RedisConnection connection) throws DataAccessException {
				connection.flushAll();
				return null;
			}
		});
	}

	@Override
	public void putSessionId(final String key, final String sessionId) {
		template.execute(new RedisCallback<Object>() {
			@Override
			public Object doInRedis(RedisConnection connection) throws DataAccessException {
				try {
					byte[] key_ = SerializeUtil.getBytesFromObject(key);
					byte[] value_ = SerializeUtil.getBytesFromObject(sessionId);
					//connection.setEx(key_, expireTime, value_);
					connection.set(key_, value_);
					return true;
				} catch (IOException e) {
					e.printStackTrace();
				}
				return false;
			}
		});
	}

	@Override
	public String getSessionId(final String key) {
		 String sessionId = (String) template.execute(new RedisCallback<Serializable>() {
			@Override
			public Serializable doInRedis(RedisConnection connection) throws DataAccessException {
				byte[] key_ = null;
				try {
					key_ = SerializeUtil.getBytesFromObject(key);
				} catch (IOException e1) {
					e1.printStackTrace();
				}
				if (connection.exists(key_)) {
					byte[] value = connection.get(key_);
					try {
						return (Serializable) SerializeUtil.getObjectFromBytes(value);
					} catch (ClassNotFoundException | IOException e) {
						e.printStackTrace();
					}
				}
				return null;
			}
		 });
		 
		 return sessionId;
	}
	
	@Override
	public void removeSessionId(final String key) {
		if (StringUtils.isBlank(key))
			throw new IllegalArgumentException("key is blank.");
		
		template.execute(new RedisCallback<Long>() {
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				byte[] key_ = null;
				try {
					key_ = SerializeUtil.getBytesFromObject(key);
				} catch (IOException e1) {
					e1.printStackTrace();
				}
				long result = connection.del(key_);
				return result;
			}
		});
	}

}
